﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ExcelAddIn1
{
    class Forecasting_ESTrendAdjustment : Test
    {
        public Forecasting_ESTrendAdjustment(DataContainer data)
        {
            this.data = data;
            res = new List<String>();
        }

        public override string GetInfo()
        {
            String s = "Forecasts a dataset using Exponential Smoothing with α-values between 0.2 and 0.8 and Trend Adjustment with β-values between 0.2 and 0.8. ";
            s += "The forecast with the lowest Mean Squared Error is shown.";
            return s;
        }

        public override void RunTest()
        {
            res.Add("Exponential Smoothing with Trend Adjustment");

            //Error check
            if (data.GetNoSets() != 1)
            {
                res.Add("One dataset is required!");
                return;
            }
            //Error check 2
            if (data.GetDataSet(0).GetN() < 4)
            {
                res.Add("Sample size of minimum 4 is required!");
                return;
            }

            res.Add("α;β;Forecast;MSE;-");

            double bA = 0.2;
            double bB = 0.2;
            double bestMSE = 0.0;
            for (double a = 0.2; a <= 0.8; a += 0.2)
            {
                for (double b = 0.2; b <= 0.8; b += 0.2)
                {
                    //Find the best MSE value
                    double cMSE = RunTest(a, b, false);
                    if (cMSE < bestMSE || bestMSE == 0.0)
                    {
                        bA = a;
                        bB = b;
                        bestMSE = cMSE;
                    }
                }
            }

            //Re-run the best forecast to output result.
            RunTest(bA, bB, true);

            res.Add(";;;;-");
        }

        public double RunTest(double alpha, double beta, bool show)
        {
            DataSet d = data.GetDataSet(0);

            //Create table
            double[,] tab = new double[d.GetN(), 6];

            tab[0, 0] = d.GetValue(0); //Actual
            tab[0, 1] = d.GetValue(0); //Forecase
            tab[0, 2] = 0; //T
            tab[0, 3] = d.GetValue(0); ; //FIT
            tab[0, 4] = 0; //Error
            tab[0, 5] = 0; //Squared Error

            int r;
            for (r = 1; r < d.GetN(); r++)
            {
                //Actual
                tab[r, 0] = d.GetValue(r);
                //Forecast
                tab[r, 1] = alpha * tab[r-1, 0] + (1.0 - alpha) * (tab[r-1, 1] + tab[r-1, 2]);
                //T
                tab[r, 2] = beta * (tab[r, 1] - tab[r - 1, 1]) + (1.0 - beta) * tab[r - 1, 2];
                //Fit
                tab[r, 3] = tab[r, 1] + tab[r, 2];
                //Error
                tab[r, 4] = tab[r, 0] - tab[r, 3];
                //Squared error
                tab[r, 5] = Math.Pow(tab[r, 4], 2);               
            }

            //Calculate final forecast
            double finalF = alpha * tab[r - 1, 0] + (1.0 - alpha) * (tab[r - 1, 1] + tab[r - 1, 2]);
            double T = beta * (finalF - tab[r - 1, 1]) + (1.0 - beta) * tab[r - 1, 2];
            finalF += T;
            
            //Calculate Mean Squared Error
            double MSE = 0.0;
            int n = 0;
            for (int i = 1; i < d.GetN(); i++)
            {
                MSE += tab[i, 5];
                n++;
            }
            MSE = MSE / (double)n;

            //If show-flag is set, print result.
            if (show) res.Add(alpha.ToString("F1") + ";" + beta.ToString("F1") + ";" + finalF.ToString("F2") + ";" + MSE.ToString("F2"));
            
            return MSE;
        }
    }
}
